package com.zhoug.audiorecorder;


import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

/**
 * wav格式的音频文件的文件头
 * @description: 类的简要描述
 * @author: 35574
 * @date: 2025/10/24 10:46
 */
public class WavHeadUtil {
    //0-3	4	ChunkID	文件标识，固定为"RIFF"	52 49 46 46 (ASCII: 'R','I','F','F')
    //4-7	4	ChunkSize	从下一个地址开始到文件尾的总字节数	(文件总大小 - 8)
    //8-11	4	Format	文件格式，固定为"WAVE"	57 41 56 45 (ASCII: 'W','A','V','E')
    //12-15	4	Subchunk1ID	格式子块标识，固定为"fmt "	66 6D 74 20 (ASCII: 'f','m','t',' ')
    //16-19	4	Subchunk1Size	格式子块的大小（不包括前8字节）	16 00 00 00 (十进制：16，表示后面有16字节的fmt数据)
    //20-21	2	AudioFormat	音频格式代码 (1 代表 PCM)	01 00 (十进制：1)
    //22-23	2	NumChannels	声道数 (1-单声道，2-立体声)	01 00 (单声道) 或 02 00 (立体声)
    //24-27	4	SampleRate	采样率 (每秒样本数，如 44100)	44 AC 00 00 (十进制：44100)
    //28-31	4	ByteRate	每秒数据字节数	SampleRate * NumChannels * BitsPerSample/8
    //32-33	2	BlockAlign	每个样本数据块的对齐字节数	NumChannels * BitsPerSample/8
    //34-35	2	BitsPerSample	每个样本的位数 (如 16bit)	10 00 (十进制：16)
    //36-39	4	Subchunk2ID	数据子块标识，固定为"data"	64 61 74 61 (ASCII: 'd','a','t','a')
    //40-43	4	Subchunk2Size	数据子块的大小 (实际音频数据的字节数)	这是音频数据部分的总字节数


    // WAV文件头常量
    private static final String RIFF_HEADER = "RIFF";
    private static final String WAVE_HEADER = "WAVE";
    private static final String FMT_HEADER = "fmt ";
    private static final String DATA_HEADER = "data";

    // PCM格式编码
    private static final short PCM_FORMAT = 1;

    /**
     * 生成WAV文件头
     *
     * @param totalAudioLen 音频数据总长度（字节）
     * @param sampleRate 采样率（Hz）
     * @param channels 声道数（1-单声道，2-立体声）
     * @param bitsPerSample 采样位数（8, 16, 24等）
     * @return WAV文件头字节数组
     */
    public static byte[] generateWavHeader(long totalAudioLen, int sampleRate,
                                           int channels, int bitsPerSample) {

        // 计算相关参数
        long byteRate = (long) sampleRate * channels * bitsPerSample / 8;
        int blockAlign = channels * bitsPerSample / 8;

        // WAV文件头总长度（标准PCM格式为44字节）
        int headerSize = 44;
        ByteBuffer buffer = ByteBuffer.allocate(headerSize);
        buffer.order(ByteOrder.LITTLE_ENDIAN); // WAV文件使用小端序

        // RIFF文件头 (12字节)
        buffer.put(RIFF_HEADER.getBytes()); // ChunkID: "RIFF"
        buffer.putInt((int) (totalAudioLen + 36)); // ChunkSize: 文件总大小 - 8
        buffer.put(WAVE_HEADER.getBytes()); // Format: "WAVE"

        // fmt 子块 (24字节)
        buffer.put(FMT_HEADER.getBytes()); // Subchunk1ID: "fmt "
        buffer.putInt(16); // Subchunk1Size: PCM格式固定为16
        buffer.putShort(PCM_FORMAT); // AudioFormat: PCM = 1
        buffer.putShort((short) channels); // NumChannels
        buffer.putInt(sampleRate); // SampleRate
        buffer.putInt((int) byteRate); // ByteRate
        buffer.putShort((short) blockAlign); // BlockAlign
        buffer.putShort((short) bitsPerSample); // BitsPerSample

        // data 子块头 (8字节)
        buffer.put(DATA_HEADER.getBytes()); // Subchunk2ID: "data"
        buffer.putInt((int) totalAudioLen); // Subchunk2Size: 音频数据长度

        return buffer.array();
    }

    /**
     * 为已录制的PCM文件添加WAV文件头
     * 适用于先录制PCM，后转换为WAV的场景
     *
     * @param pcmFile PCM文件路径
     * @param wavFile WAV文件路径
     * @param sampleRate 采样率
     * @param channels 声道数
     * @param bitsPerSample 采样位数
     * @throws IOException 文件操作异常
     */
    public static void pcmToWav(String pcmFile, String wavFile,
                                int sampleRate, int channels, int bitsPerSample) throws IOException {

        RandomAccessFile pcmRaf = null;
        RandomAccessFile wavRaf = null;

        try {
            pcmRaf = new RandomAccessFile(pcmFile, "r");
            wavRaf = new RandomAccessFile(wavFile, "rw");

            // 获取PCM文件长度
            long pcmDataLength = pcmRaf.length();

            // 生成WAV文件头
            byte[] header = generateWavHeader(pcmDataLength, sampleRate, channels, bitsPerSample);

            // 写入WAV文件头
            wavRaf.write(header);

            // 复制PCM数据
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = pcmRaf.read(buffer)) != -1) {
                wavRaf.write(buffer, 0, bytesRead);
            }

        } finally {
            if (pcmRaf != null) {
                try {
                    pcmRaf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (wavRaf != null) {
                try {
                    wavRaf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * 实时录音时写入初始WAV文件头（数据长度为0）
     * 录音完成后需要调用updateWavHeader更新实际数据长度
     *
     * @param file 要写入的文件
     * @param sampleRate 采样率
     * @param channels 声道数
     * @param bitsPerSample 采样位数
     * @throws IOException 文件操作异常
     */
    public static void writeInitialWavHeader(RandomAccessFile file,
                                             int sampleRate, int channels,
                                             int bitsPerSample) throws IOException {

        // 初始写入数据长度为0的文件头
        byte[] header = generateWavHeader(0, sampleRate, channels, bitsPerSample);
        file.write(header);
    }


    /**
     * 更新WAV文件头中的音频数据长度信息
     * 在录音完成后调用此方法
     *
     * @param file 要更新的文件
     * @param totalAudioLen 实际的音频数据长度
     * @throws IOException 文件操作异常
     */
    public static void updateWavHeader(RandomAccessFile file, long totalAudioLen) throws IOException {
        if (file.length() < 44) {
            throw new IOException("File too small to be a valid WAV file");
        }

        // 使用ByteBuffer来以小端序写入整数
        ByteBuffer buffer = ByteBuffer.allocate(8);
        buffer.order(ByteOrder.LITTLE_ENDIAN);

        // 更新ChunkSize (文件总大小 - 8)
        file.seek(4);
        buffer.putInt(0, (int) (totalAudioLen + 36));
        file.write(buffer.array(), 0, 4);

        // 更新Subchunk2Size (音频数据长度)
        file.seek(40);
        buffer.putInt(0, (int) totalAudioLen);
        file.write(buffer.array(), 0, 4);
    }

    /**
     * 调试方法：打印WAV文件头信息
     */
    public static String debugWavHeader(File wavFile) {
        try (RandomAccessFile raf = new RandomAccessFile(wavFile, "r")) {
            byte[] header = new byte[44];
            raf.read(header);
            ByteBuffer buffer = ByteBuffer.wrap(header);
            buffer.order(ByteOrder.LITTLE_ENDIAN);
            // 读取关键参数
            String chunkID = new String(header, 0, 4);
            int chunkSize = buffer.getInt(4);
            String format = new String(header, 8, 4);
            String subchunk1ID = new String(header, 12, 4);
            int subchunk1Size = buffer.getInt(16);
            short audioFormat = buffer.getShort(20);
            short numChannels = buffer.getShort(22);
            int sampleRate = buffer.getInt(24);
            int byteRate = buffer.getInt(28);
            short blockAlign = buffer.getShort(32);
            short bitsPerSample = buffer.getShort(34);
            String subchunk2ID = new String(header, 36, 4);
            int subchunk2Size = buffer.getInt(40);
            StringBuilder logBuilder = new StringBuilder();

            logBuilder.append("=== WAV文件头调试信息 ===");
            logBuilder.append("文件大小: ").append(wavFile.length()).append(" 字节").append("\n");
            logBuilder.append("ChunkID: ").append(chunkID).append("\n");
            logBuilder.append("ChunkSize: ").append(chunkSize).append("\n");
            logBuilder.append("Format: ").append(format).append("\n");
            logBuilder.append("Subchunk1ID: ").append(subchunk1ID).append("\n");
            logBuilder.append("Subchunk1Size: ").append(subchunk1Size).append("\n");
            logBuilder.append("AudioFormat: ").append(audioFormat).append(" (1=PCM)").append("\n");
            logBuilder.append("声道数: ").append(numChannels).append("\n");
            logBuilder.append("采样率: ").append(sampleRate).append(" Hz").append("\n");
            logBuilder.append("字节率: ").append(byteRate).append(" 字节/秒").append("\n");
            logBuilder.append("块对齐: ").append(blockAlign).append(" 字节").append("\n");
            logBuilder.append("采样位数: ").append(bitsPerSample).append(" bit").append("\n");
            logBuilder.append("Subchunk2ID: ").append(subchunk2ID).append("\n");
            logBuilder.append("音频数据大小: ").append(subchunk2Size).append(" 字节").append("\n");

            // 验证计算
            int expectedByteRate = sampleRate * numChannels * bitsPerSample / 8;
            int expectedBlockAlign = numChannels * bitsPerSample / 8;
            logBuilder.append("预期字节率: ").append(expectedByteRate).append(" 字节/秒").append("\n");
            logBuilder.append("预期块对齐: ").append(expectedBlockAlign).append(" 字节").append("\n");
            return logBuilder.toString();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


}
